#pragma once
#include <memory>
#include "VectorMath.h"
#include "VectorHelper.h"

using namespace VectorHelper;

#define EXPORTCLASS _declspec(dllexport) 

union EXPORTCLASS Vector2
{
	struct 
	{
		float x, y;
	};
	Vector2(void)
	{
		memset(this, 0, sizeof(Vector2));
	}
	Vector2(float* e)
	{
		memcpy(this, e, sizeof(Vector2));
	}
	Vector2(float xy)
	{
		x = y = xy;
	}
	Vector2(float x, float y)
	{
		this->x = x;
		this->y = y;
	}

	operator float*() { return reinterpret_cast<float*>(this); }

	operator const float*(void) const { return reinterpret_cast<const float*>(this); }

	void Normalize(void);

	float getLength(void) const { return sqrt(x*x + y*y); }

	Vector2& operator +=(Vector2& other);
	Vector2& operator -=(Vector2& other);

private:
	float e[2];
};

union EXPORTCLASS Vector3
{
	struct
	{
		float x, y, z;
	};

	Vector3(void)
	{
		memset(this, 0, sizeof(Vector3));
	}
	Vector3(float* e)
	{
		memcpy(this, e, sizeof(Vector3));
	}
	Vector3(float xyz)
	{
		memset(this, 0, sizeof(Vector3));
		x = y = z = xyz;
	}
	Vector3(float x, float y, float z)
	{
		this->x = x;
		this->y = y;
		this->z = z;
	}
	Vector3(Vector2& v, float z)
	{
		x = v.x;
		y = v.y;
		this->z = z;
	}

	void setXy(Vector2& xy) { x = xy.x, y = xy.y; }

	float getLength(void) const { return sqrt(x*x + y*y + z*z); }

	Vector2 getXy(void) { return Vector2(x, y); }

	void Normalize(void);
	Vector3& operator +=(Vector3& other);
	Vector3& operator -=(Vector3& other);

	operator float*() { return reinterpret_cast<float*>(this); }

private:
	float e[3];
};

union EXPORTCLASS Vector4
{
	struct
	{
		float x, y, z, w;
	};

	Vector4(void)
	{
		memset(this, 0, sizeof(Vector4));
	}
	Vector4(float* e) 
	{
		memcpy(this, e, sizeof(Vector4));
	}
	Vector4(float xyzw)
	{
		x = y = z = w = xyzw;
	}
	Vector4(float x, float y, float z, float w)
	{
		this->x = x;
		this->y = y;
		this->z = z;
		this->w = w;
	}
	Vector4(Vector3& v, float w)
	{
		setXyz(v);
		this->w = w;
	}

	void setXy(Vector2& xy) 
	{ 
		x = xy.x; 
		y = xy.y; 
	}
	void setXyz(Vector3& xyz)
	{
		setXy(xyz.getXy());
		z = xyz.z;
	}

	Vector2 getXy(void) { return Vector2(x, y); }
	Vector3 getXyz(void) { return Vector3(x, y, z); }

	float getLength(void) const { return sqrt(x*x + y*y + z*z + w*w); }

	operator float*() { return reinterpret_cast<float*>(this); }

	operator const float*() const { return reinterpret_cast<const float*>(this); }

	Vector4& operator +=(Vector4& other);
	Vector4& operator -=(Vector4& other);
	void Normalize(void);

private:
	float e[4];
};

_declspec(align(16)) union EXPORTCLASS Matrix
{
	float e[16];

	struct
	{
		float m11, m12, m13, m14,
			m21, m22, m23, m24,
			m31, m32, m33, m34,
			m41, m42, m43, m44;
	};

	struct
	{
		Vector4 m1, m2, m3, m4;
	};

	struct
	{
		Vector4 x_axis, y_axis, z_axis, position;
	};

	Matrix(void)
	{
		memset(this, 0, 64);
	}
	Matrix(Vector4& m1, Vector4& m2, Vector4& m3, Vector4& m4)
	{
		memcpy(e, m1, 16);
		memcpy(e + 4, m2, 16);
		memcpy(e + 8, m3, 16);
		memcpy(e + 12, m4, 16);
	}
	Matrix(float m11, float m12, float m13, float m14,
		float m21, float m22, float m23, float m24,
		float m31, float m32, float m33, float m34,
		float m41, float m42, float m43, float m44)
	{
		this->m11 = m11;
		this->m12 = m12;
		this->m13 = m13;
		this->m14 = m14;

		this->m21 = m21;
		this->m22 = m22;
		this->m23 = m23;
		this->m24 = m24;

		this->m31 = m31;
		this->m32 = m32;
		this->m33 = m33;
		this->m34 = m34;

		this->m41 = m41;
		this->m42 = m42;
		this->m43 = m43;
		this->m44 = m44;
	}


};

union Quaternion
{
	struct
	{
		float w, x, y, z;
	};

	Quaternion(void)
	{
		memset(this, 0, sizeof(Quaternion));
	}
	Quaternion(float w, float x, float y, float z)
	{
		this->x = x;
		this->y = y;
		this->z = z;
		this->w = w;
	}
	Quaternion(Vector3& vector, float angle)
	{
		memcpy(this, vector, sizeof(Vector3));
		w = angle;
	}
	Quaternion(Vector4& vector)
	{
		memcpy(this, vector, sizeof(Quaternion));
	}
	Quaternion(float* e)
	{
		memcpy(this, e, 16);
	}

	Vector3 getVector(void) const { return Vector3(x, y, z); }
	void setVector(Vector3& value) { memcpy(this, value, sizeof(Vector3)); }

	operator float*(void) { return reinterpret_cast<float*>(this); }

	Quaternion operator +=(Quaternion&);
	Quaternion operator -=(Quaternion&);

	void Normalize(void);

private:
	float e[4];
};